package cn.tedu.v2_1;

import java.io.*;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;

/**
 *  Version2.1  多人聊天室
 *              功能：知道客户端IP，打印服务器处
 *
 * 启动服务器---N多客户端连接服务器---accept【返回一个客户端Socket】
 * 缺点：只能知道来人IP，并不知道那个IP在服务器段说话
 *
 * 调整：获取IP地址的方法 移动---CS多线程中处理！
 *
 * 思路：1.删除N多客户端链接服务器--打印IP的消息【证明连接上】
 *      2.获取客户端IP信息---存入线程CS类中：
 *        处理多个客户端链接可否返回【客户端：】
 *        CS构造器-->start方法---创建对象---传入客户端socket
 *                                       并且赋值当前线程Socket对象
 *         【保证一个客户端链接服务器，正确返回，等同于找到正确客户端】
 *       ①私有化ip目的：保证IP信息正确，供当前CS类使用
 *       ②构造器：完成ip的获取方式：创建CS线程，明确客户端socket
 *                                  立即给客户端进行IP赋值
 *       socket.getInetAddress()  获取主机IP信息对象
 *       socket.getInetAddress().getHostAddress();  获取主机IP子字符串
 *
 *       ----------------------------------------------------------------
 * 优化操作：针对【SeverSocket类与Socket 程序健壮性】
 * 优化操作：SeverSocket  运行在服务器socket----关闭问题
 * 关闭方法：服务器端mian、start 方法中finally关闭
 * 为什么在以上两个位置关闭，服务器通讯类：
 * 因为关闭后将无法在接受任何的客户端的链接【所有功能提供完毕】
 * severSocket!=null  关闭通讯类--判断是否为对象null
 * isClosed() 判断SeverSocket（Socket）对象是否关闭
 * 默认情况：true，false表示没有关闭
 *
 * CS线程类：处理各个客户端
 * CS线程中Socket  通讯类【同IO流操作同在】 ----关闭问题
 * 判断逻辑：socket！=null     对象是否被赋值
 *                     缺点：空指针异常
 *         !socket.isClosed  对象没有被关闭
 *  socket！=null&&socket.isClosed 解释如下：
 *  socket已经被赋值并且socket没有关闭---需要关闭
 *  关闭位置：流对应的finally块中
 */
public class Server {
    private ServerSocket serverSocket;
    public Server(){
        try {
            serverSocket=new ServerSocket(1268);
        } catch (BindException e) {
            System.out.println("请检查端口号，被占用");
        }
        catch (IOException e) {
            System.out.println("客户端没有链接上");
        }
    }

    //3.创建 start 方法accept（）1.一直等待客户端连接！
    //                         2.传输数据：IO流交给CS线程类
    public  void start()  {
        Socket socket = null;
        String ip=null;
        try {
         while (true) {
          System.out.println("等待客户端连接");
            socket = serverSocket.accept();


            new Thread( new CS(socket)).start();

             }
          }
          catch (IOException e) {
           System.out.println(ip+"客户端链接断开链接");
           } finally {
           //1.正确关闭severSocket--->start方法中
           //  不为空：是否初始化   尚未关闭：检查对象是否关闭
            if (serverSocket!=null&&serverSocket.isClosed()){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    System.out.println("服务器关闭失败");
                }
                System.out.println("服务器关闭成功");
            }
         }
    }

    //4.创建主方法---服务器程序启动
    public static void main(String[] args)  {
        Server server=new Server();
        server.start();
    }


    private class CS implements Runnable{

        private  Socket  socket;

        //一、客户端IP在说话同时打印服务器窗口
        //1.在CS线程中添加ip的私有属性【封装：本类使用，ip准确】
        private String ip;
        public CS(Socket s){
            this.socket=s;
            //3.CS线程对象：IP赋值
            ip=socket.getInetAddress().getHostAddress();

        }


        public void run() {
          BufferedReader br=null;
          try {
              br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF8"));

              while (true) {
                  String str = br.readLine();
                  System.out.println(ip+"客户端：" + str);
              }
          } catch (UnsupportedEncodingException e) {
            System.out.println("请检查客户端/服务器端编码集：不一致");
          }catch (SocketException e){
              System.out.println("客户端链接被重置");
          }
            catch (IOException e) {
            System.out.println("客户端链接失效");
          } finally {//一定要关闭：先关闭流、后关闭Socket
            //关闭流  关闭socket
            if (br!=null)
                try {
                    br.close();
                } catch (IOException e) {
                    System.out.println("读入流断开连接");
                }
            //2.关闭CS多线程类中---运行的socket通讯类
              if (socket!=null&&!socket.isClosed()){
                  System.out.println(ip+"客户端退出服务器链接");
                  try {
                      socket.close();
                  } catch (IOException e) {
                      System.out.println(ip+"客户端退出服务器链接");
                  }
              }
         }
      }
   }
}
